#!/usr/bin/env node

import { program } from 'commander';
import fs from 'fs';
import https from 'https';
import fetch, { FormData } from 'node-fetch';
import path from 'path';
import builder_package from './builder_package.js';
import builder_web from './builder_web.js';

const { version } = JSON.parse(fs.readFileSync('./package.json', 'utf-8'));

const dir = process.env.INIT_CWD;
const project_json = JSON.parse(fs.readFileSync(process.env.npm_package_json, 'utf-8'));
if (!project_json.id) throw Error(`'package.json'中需要设置id`);
if (!project_json.targetName) throw Error(`'package.json'中需要设置targetName`);

// 解析命令行参数
program
  .version(version)
  .option('-B, --build <type>', 'Build the specified contents. type can be package|web|all')
  .option('-D, --deploy <type>', 'Deploy specified contents to Remote Host. type can be package|web|all')
  .option('-c, --card <file>', 'Specified the single card file when Deploy to Remote Host. Only affective with --deploy=package')
  .option('-j, --job <file>', 'Specified the single job file when Deploy to Remote Host. Only affective with --deploy=package')
  .option('-p, --page <file>', 'Specified the single page file when Deploy to Remote Host. Only affective with --deploy=package')
  .option('-d, --data <file>', 'Specified the single data file when Deploy to Remote Host. Only affective with --deploy=package')
  .showHelpAfterError('(add -h for additional information)')
  .parse(process.argv);

onCommand(program.opts());

async function onCommand(opts) {
  const context = {};

  // 处理本地打包
  if (opts.build) {
    context.package = await builder_package(dir, project_json);

    if (!fs.existsSync(path.join(dir, 'dist'))) {
      fs.mkdirSync(path.join(dir, 'dist'));
    }
    fs.writeFileSync(path.join(dir, 'dist', project_json.targetName + '.json'), JSON.stringify(context.package));
    console.log('\nPACKAGE BUILD SUCCESS!\n');
  }

  if (opts.deploy) {
    // 加载server.config.js的部署配置
    const { default: { host: _host, key } } = await import("file://" + path.join(dir, 'server.config.js'));
    if (!_host || !key) throw Error("server.config.js中缺失host或key配置项");
    const host = _host.endsWith('/') ? _host : _host + '/';

    const deploy_package = async () => {
      if (!context.package) {
        const package_txt = fs.readFileSync(path.join(dir, 'dist', project_json.targetName + '.json'), { encoding: "utf-8" });
        context.package = JSON.parse(package_txt);
      }
      try {
        await deploy(context.package, Boolean(opts.card || opts.job || opts.page || opts.data));
        console.log("\nPACKAGE DEPLOY SUCCESS!\n");
      } catch (e) {
        console.error(e);
      }
    };
    const deploy_web = async () => {
      const { Zip, ...values } = await builder_web(dir, project_json);
      try {
        const [zip_path] = await upload('前端部署包', 'web.zip', Zip);
        await deploy({ ...values, Zip: zip_path }, true);
        console.log("\nWEB DEPLOY SUCCESS!\n");
      } catch (e) {
        console.error(e);
      }
    };

    switch (opts.deploy) {
      case 'package': {
        if (opts.card || opts.job || opts.page || opts.data) {
          // 覆盖package
          context.package = await builder_package(dir, project_json, opts.card, opts.job, opts.page, opts.data);
        }
        await deploy_package();
        break;
      }
      case 'web': {
        await deploy_web();
        break;
      }
      case 'all': {
        await deploy_package();
        await deploy_web();
        break;
      }
    }

    async function getToken() {
      const res = await fetch(`${host}api/job`, {
        method: 'POST',
        agent: host.startsWith('https:') ? new https.Agent({
          rejectUnauthorized: false,
        }) : undefined,
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          Name: 'DeployToken',
          Param: { Key: key },
        })
      });
      if (res.ok) {
        return await res.json();
      }
      let text = await res.text();
      if (res.status === 404) {
        throw Error(res.status + ' ' + res.statusText);
      } else {
        if (text) throw Error(text)
        else throw Error(res.status + ' ' + res.statusText);
      }
    }

    async function upload(dir, file_name, file_data) {
      const { token } = await getToken();

      const form = new FormData();
      form.append('dir', dir);
      form.append('files', file_data, file_name);

      const res = await fetch(`${host}api/doc`, {
        method: 'POST',
        agent: host.startsWith('https:') ? new https.Agent({
          rejectUnauthorized: false,
        }) : undefined,
        headers: {
          'Authorization': 'Bearer ' + token
        },
        body: form
      });
      if (res.ok) {
        return await res.json();
      }
      let text = await res.text();
      if (res.status === 404) {
        throw Error(res.status + ' ' + res.statusText);
      } else {
        if (text) throw Error(text)
        else throw Error(res.status + ' ' + res.statusText);
      }
    }

    async function deploy(pack, part) {
      console.log(`deploying to ${host}`);
      const { token } = await getToken();
      const res = await fetch(`${host}api/job`, {
        method: 'POST',
        agent: host.startsWith('https:') ? new https.Agent({
          rejectUnauthorized: false,
        }) : undefined,
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + token
        },
        body: JSON.stringify({
          Name: 'Deploy',
          Param: { ID: pack.ID, Package: pack, Partial: Boolean(part) },
        })
      });
      const text = await res.text();
      if (res.ok) {
        return text;
      } else {
        throw Error(text);
      }
    }
  }
}